package de.otto.wickettester; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import java.io.Serializable; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.apache.wicket.Component; import org.apache.wicket.MarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.util.tester.FormTester; import org.apache.wicket.util.tester.TagTester; import org.apache.wicket.util.tester.WicketTester; import org.apache.wicket.util.visit.IVisit; import org.apache.wicket.util.visit.IVisitor; import org.apache.wicket.util.visit.Visits; import org.testng.Assert; import de.otto.wickettester.ComponentMatchers.ComponentMatcherBuilder; import de.otto.wickettester.ComponentMatchers.ComponentMatcherBuilder.CollectingComponentMatcher; public class EnhancedWicketTester extends WicketTester { public EnhancedWicketTester() { super(); } public EnhancedWicketTester(final WebApplication application) { super(application); } public static <T extends Component, R> R visitComponentTree(final MarkupContainer root, final ComponentMatcher<T, R> matcher) { final R res = Visits.visitChildren(root, new IVisitor<T, R>() { @Override public void component(final T component, final IVisit<R> visit) { final R result = matcher.match(component); if (result != null) { visit.stop(result); } } }); return res; } public <T extends Component> List<T> getChildrenMatching(final MarkupContainer root, final ComponentMatcherBuilder<T> builder, final ComponentMatcherBuilder<? extends MarkupContainer>... parentBuilders) { final ComponentMatcherBuilder<? extends MarkupContainer>[] realParentBuilders = cleanup(parentBuilders); if (realParentBuilders.length == 0) { final CollectingComponentMatcher<T, T> matcher = builder.buildCollecting(); visitComponentTree(root, matcher); return matcher.getBucket(); } else { final List<? extends MarkupContainer> parentsMatching = getChildrenMatching(root, realParentBuilders[0], tail(realParentBuilders)); final List<T> childrenMatching = new LinkedList<T>(); for (final Component parent : parentsMatching) { final CollectingComponentMatcher<T, T> matcher = builder.buildCollecting(); visitComponentTree((MarkupContainer) parent, matcher); childrenMatching.addAll(matcher.getBucket()); } return childrenMatching; } } @SuppressWarnings("unchecked") private ComponentMatcherBuilder<? extends MarkupContainer>[] cleanup( final ComponentMatcherBuilder<? extends MarkupContainer>[] builders) { if (builders == null || builders.length == 0) { return new ComponentMatcherBuilder[0]; } // remove null values ComponentMatcherBuilder<? extends MarkupContainer>[] buildersWip = builders; while (ArrayUtils.contains(buildersWip, null)) { buildersWip = (ComponentMatcherBuilder<? extends MarkupContainer>[]) ArrayUtils.removeElement(builders, (Object) null); } return buildersWip; } @SuppressWarnings("unchecked") private ComponentMatcherBuilder<? extends MarkupContainer>[] tail( final ComponentMatcherBuilder<? extends MarkupContainer>[] arr) { return (ComponentMatcherBuilder<? extends MarkupContainer>[]) ArrayUtils.remove(arr, 0); } @SuppressWarnings("unchecked") public <T extends Component> List<T> getChildrenMatching(final MarkupContainer root, final ComponentMatcherBuilder<T> builder) { return getChildrenMatching(root, builder, (ComponentMatcherBuilder<? extends MarkupContainer>) null); } public <T extends Component> List<T> getChildrenMatching(final ComponentMatcherBuilder<T> builder, final ComponentMatcherBuilder<? extends MarkupContainer>... parentBuilders) { return getChildrenMatching(getLastRenderedPage(), builder, parentBuilders); } @SuppressWarnings("unchecked") public <T extends Component> List<T> getChildrenMatching(final ComponentMatcherBuilder<T> builder) { return getChildrenMatching(builder, (ComponentMatcherBuilder<? extends MarkupContainer>) null); } public <T extends Component> T getChildMatching(final MarkupContainer root, final ComponentMatcherBuilder<T> builder, final ComponentMatcherBuilder<? extends MarkupContainer>... parentBuilders) { final List<T> children = getChildrenMatching(root, builder, parentBuilders); Assert.assertEquals(children.size(), 1, String.format("Did not find exactly one child %s", builder.criteriaAsString())); return children.get(0); } @SuppressWarnings("unchecked") public <T extends Component> T getChildMatching(final MarkupContainer root, final ComponentMatcherBuilder<T> builder) { return getChildMatching(root, builder, (ComponentMatcherBuilder<? extends MarkupContainer>) null); } public <T extends Component> T getChildMatching(final ComponentMatcherBuilder<T> builder, final ComponentMatcherBuilder<? extends MarkupContainer>... parentBuilders) { return getChildMatching(getLastRenderedPage(), builder, parentBuilders); } @SuppressWarnings("unchecked") public <T extends Component> T getChildMatching(final ComponentMatcherBuilder<T> builder) { return getChildMatching(builder, (ComponentMatcherBuilder<? extends MarkupContainer>) null); } public <T extends Component> T getFirstChildMatching(final MarkupContainer root, final ComponentMatcherBuilder<T> builder, final ComponentMatcherBuilder<? extends MarkupContainer>... parentBuilders) { final List<T> children = getChildrenMatching(root, builder, parentBuilders); Assert.assertTrue(children.size() > 0, String.format("Did not find at least one child %s", builder.criteriaAsString())); return children.get(0); } public <T extends Component> T getFirstChildMatching(final ComponentMatcherBuilder<T> builder, final ComponentMatcherBuilder<? extends MarkupContainer>... parentBuilders) { return getFirstChildMatching(getLastRenderedPage(), builder, parentBuilders); } @SuppressWarnings("unchecked") public <T extends Component> T getFirstChildMatching(final MarkupContainer root, final ComponentMatcherBuilder<T> builder) { return getFirstChildMatching(root, builder, (ComponentMatcherBuilder<? extends MarkupContainer>) null); } @SuppressWarnings("unchecked") public <T extends Component> T getFirstChildMatching(final ComponentMatcherBuilder<T> builder) { return getFirstChildMatching(builder, (ComponentMatcherBuilder<? extends MarkupContainer>) null); } public String getPathRelativeToRoot(final Component root, final Component component) { final String rootPath = root.getPageRelativePath(); final String componentPath = component.getPageRelativePath(); Assert.assertTrue(componentPath.startsWith(rootPath), "Component is not a child of the root component"); if (rootPath.length() == componentPath.length()) { return StringUtils.EMPTY; } /* * remove the root path prefix and any possible leftover path * separators. */ return StringUtils.stripStart(componentPath.substring(rootPath.length()), ":"); } public String getPathRelativeToPage(final Component component) { return getPathRelativeToRoot(getLastRenderedPage(), component); } public TagTester getTagTesterByComponent(final Component component) { final String[] pathSegments = component.getPath().split(String.valueOf(Component.PATH_SEPARATOR)); //strip leading 0: final String[] tail = Arrays.copyOfRange(pathSegments, 1, pathSegments.length); return TagTester.createTagByAttribute(getLastResponseAsString(), "wicketpath", getWicketPath(tail)); } public TagTester getTagTesterByComponentMatcher(final ComponentMatcherBuilder<? extends Component> builder, final ComponentMatcherBuilder<? extends MarkupContainer>... parentBuilders) { return getTagTesterByComponent(getChildMatching(builder, parentBuilders)); } private String getWicketPath(final String[] tail) { for (int i = 0; i < tail.length; i++) { tail[i] = tail[i].replace("_", "__"); } return StringUtils.join(tail, "_"); } public void assertModelValue(final Component component, final Object expectedModelObject) { assertThat(component.getDefaultModelObject(), equalTo(expectedModelObject)); } public void assertModelValue(final Object expectedModelObject, final ComponentMatcherBuilder<? extends Component> builder, final ComponentMatcherBuilder<? extends MarkupContainer>... parentBuilders) { assertThat(getChildMatching(builder, parentBuilders).getDefaultModelObject(), equalTo(expectedModelObject)); } public void assertVisible(final Component component) { assertVisible(getPathRelativeToPage(component)); } public void assertVisible(final ComponentMatcherBuilder<? extends Component> builder, final ComponentMatcherBuilder<? extends MarkupContainer>... parentBuilders) { assertVisible(getChildMatching(builder, parentBuilders)); } public void assertFeedback(final ComponentMatcherBuilder<? extends Component> builder, final Serializable... feedback) { assertFeedback(getPathRelativeToPage(getChildMatching(builder)), feedback); } public FormTester newFormTester(final Form<?> form) { return newFormTester(getPathRelativeToPage(form)); } public FormTester newFormTester(@SuppressWarnings("rawtypes") final ComponentMatcherBuilder<? extends Form> builder, final ComponentMatcherBuilder<? extends MarkupContainer>... parentBuilders) { return newFormTester(getChildMatching(builder, parentBuilders)); } public void assertInvisible(final Label component) { assertInvisible(getPathRelativeToPage(component)); } public void clickLink(final ComponentMatcherBuilder<? extends Component> builder, final ComponentMatcherBuilder<? extends MarkupContainer>... parentBuilders) { clickLink(getChildMatching(builder, parentBuilders)); } public void executeAjaxEvent(final String event, final ComponentMatcherBuilder<? extends Component> builder, final ComponentMatcherBuilder<? extends MarkupContainer>... parentBuilders) { executeAjaxEvent(getChildMatching(builder, parentBuilders), event); } }